home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-03
/
dosbasic.zip
/
FHELP.DOC
< prev
next >
Wrap
Text File
|
1990-12-19
|
30KB
|
620 lines
;
;DOS, Equipment and File Utilities
=======================================================================
Copyright (C) Copr. 1990 by Sidney J. Kelly
All Rights Reserved.
Sidney J. Kelly
150 Woodhaven Drive
Pittsburgh, PA 15228
home phone 412-561-0950 (7pm to 9:30pm EST)
CompuServe 70043,1656
Genie S.KELLY8
=======================================================================
These routines provide information concerning the hardware/software
environment. A few routines are duplicated in PDS 7.1/QBX, though not with
the same parameters. Some routines provide information about the DOS
status of a drive or file names. Some routines even return information
without needing ON ERROR GOSUB. A fair number of hardware routines are
provided.
Because I do not have access to PDS 7.x/QBX and its far string
library, I believe that any of the routines that obtain input or make output
to strings will not work correctly. Some MASM code is shown below to
demonstrate how to fix the routines so that they will do the same thing inside
PDS 7.x/QBS. Jim Mack has released some routines for PDS on COMPUSERVE MSSYS
TRIMS.ASM. It shows how to pass far strings inside PDS.
=================================================================
DECLARE FUNCTION ACTUALEXTND% ()
Returns:
Actual amount of extended memory installed on 80286, 80386 or 80486
machine as stored in CMOS RAM
If clock battery is bad, will return a -1
=================================================================
DECLARE FUNCTION ANSICHECK%()
IF ANSICHECK% THEN
PRINT "ANSI.SYS is installed."
Source: Disassembled COMMAND.COM Version 3.3 of CLS command
xxxx:2B62h is the beginning of the routine
VERY FAST and NOT MESSY!!!!
This is the same method that COMMAND.COM uses to test for ANSI.SYS
so every utility had better allow for this testing method.
NOTE: PC Magazine's ANSI.COM even if turned off will still report ANSI.SYS
present, (it was planned that way so it would handle CLS).
=================================================================
DECLARE SUB BEEPER BEEPER
Primarily used to show you how to make sound in a somewhat machine
independent basis
=================================================================
DECLARE FUNCTION CHECK87%()
Returns:
0 if no 80x87, or system equipment word not set.
87 if an 8087
-87 if 8087 emulation in use on an 80286 or 80386. Because the
80287 has almost exactly the same instruction set as the
8087, few have felt a need to emulate the 80287. I have not
seen an 80387 emulator in software. The 80827 just includes
one new instruction (protected mode), nothing for use in
real mode. 80387 has transcendental math routines.*
287 if an 80827
387 if an 80387 or 80487
More accurate than checking the equipment word. QBX, Version 7
merely checks the equipment word to determine if an 80x87 is installed.
I guess that can be used as a software toggle.
* Intel recently released an 80287 that has 80387 transcendental math
routines in it. If someone sends me a new chip, I will write code
to test for it.
Because of complaints about the traditional test with inexpensive clones
(See Jon Waterhouse letter in Byte, Nov. 1990, page 40)
routine first tests the equipment word in RAM bios.
=================================================================
DECLARE FUNCTION DOSVER% ()
CALL DOSVER%
Returns DOS VERSION value as an integer, to get display value \ 100
E.g. DOS Version 3.3 is returned as 330
=================================================================
DECLARE SUB DRVSPACE (DRIVE$,SPACE&)
checks if DRIVE$ <"A" or >"z", or if >"Z" & less <"a"
Drive of "@" gives the current drive
Invalid DRIVE$ gives a free space of 0
Note: DOS does not correctly adjust this if use JOIN or SUBST and
try to determine the size of a non-default drive.
=================================================================
DECLARE SUB DRIVEALIAS ( ASSIGN%, DAPPEND%, NETWORK%, SHARE%)
CALL DRIVEALIAS( ASSIGN%, DAPPEND%, NETWORK%, SHARE%)
Purpose: To warn programmer that the logical drives may not be as they
seem, so if programmer desires to do some technical manipulation
he will be aware that it may not work.
=================================================================
DECLARE SUB EQUIPMENT(ConvMem%,PrinterPorts%,ComPorts%)
Returns amount of Conventional Memory in KB number of Parallel Ports
number of COM ports
=================================================================
DECLARE SUB EXIST (FILNAME$, ErrCode%, Mode%)
CALL EXIST(FILENAME$+CHR$(0), ErrCode%, Mode%)
Filename$ must be an ASCII Null string (end in CHR$(0)).
Filename$ can not be a TYPEd string or a variable in a string array
because there is a chance that string will be outside DGROUP. String
cocentenation is used to make sure it is in DGROUP.
Mode%:
= 0 if just want to see if file exits
<> 0 if want to see if can read and write to file
Returns ErrCode% = 0 (False) if no error
= True if an error,
Codes:
-1 = string wrong length
2 = file not found
3 = path not found
4 = no free handles
5 = access denied (tried to read a subdirectory)
12 = invalid access code (wont see this unless Mode% <> 0)
20 = Write protect error (wont see this)
21 = Invalid drive
22 = Drive not ready (floppy drive door open)
23 = unknown command
24 = CRC error
25 = Bad request structure
26 = Seek error
27 = Unknown disk format
28 = Sector not found
29 = Printer out of paper (shouldn't see this)
30 = Write fault (shouldn't see this)
31 = Read fault
32 = General, non-specific error (usually a network drive error)
35 = Invalid Disk Change (shouldn't see this)
=================================================================
DECLARE FUNCTION FINDDRIVES% ()
Returns number of current logical drives w/o any errors
Because LASTDRIVE default value = 5, it is likely that
number of logical drives will be less than LASTDRIVE in CONFIG.SYS
This routine will miss a drive if there are gaps between logical drives
as can occur if SUBST is used.
=================================================================
DECLARE SUB FLOPPYREADY (DRIVE$, ErrCode%)
Tests if floppy drive is ready, on a one floppy system
treats drive B: as equivalent to drive A:
ErrCode
128 = Time Out Error 80 = Track error
-1 = Drive$ is not valid
0 = All a.o.k.
Note : A critical error routine is not necessary for this routine to work
=================================================================
DECLARE SUB FLOPPYDRIVES(NumDrives%)
Returns number of physical floppy drives in system
=================================================================
DECLARE FUNCTION GETCPU% ()
Checks for CPU type
Returns:
88 for 8088
86 for 8086
20 for NEC V20
30 for NEC V30
186 for 80186 or 80188 (I have never seen an 80188 so no special test)
286 for 80286
386 for 80386DX or 80386SX
386 for 80486. I offer no special test for 80486.
=================================================================
DECLARE SUB GETCURRENTNAME (FName as STRING * 64, FileLength%)
Returns current filename for dos version 3.xx and above
if LEN(FName$) <> 64 then returns a FileLength% of -1
FName$ can not be a TYPEd string or a variable in a string array
because there is a chance that string will be outside DGROUP.
This routine assumes that FName$ is a near string.
Function returns an error if DOS version < 3.xx since function is not
supported for that version.
=================================================================
DECLARE SUB GETDOSVER (VERSION$)
VERSION$=SPACE$(4)
CALL GETDOSVER(VERSION$)
Returns DOS Version as a string
=================================================================
DECLARE SUB GETDRIVE(DRIVE$)
CALL GETDRIVE(DRIVE$)
Returns current drive name
If LEN(DRIVE$)=0 then nothing happens
Assumes that DRIVE$ is a near string, not TYPEd or part of a far string
array. In other words, assumes DRIVE$ is in DGROUP
=================================================================
DECLARE SUB GETFULLPATH (PATH$, PATHLEN%)
Returns current subdirectory path, with Drive:\, e.g. "A:\"
if LEN(Path$) <> 67 then returns a pathlen% of -1
PATH$ can not be a TYPEd string or a variable in a string array because there is a chance that string will be outside DGROUP.
This routine assumes that Path$ is a near string.
=================================================================
DECLARE SUB GETSUB (PATH AS STRING * 64, PATHLEN%)
Returns current subdirectory path, without Drive:\, (i.e. no "A:\")
if LEN(Path$) not = to 64 then returns a pathlen% of -1
PATH$ can not be a TYPEd string or a variable in a string array
because there is a chance that string will be outside DGROUP.
This routine assumes that Path$ is a near string.
=================================================================
DECLARE SUB HARDDRIVES(NoDrives%)
Returns number of physical hard disks in system
=================================================================
DECLARE SUB KILLPRINT (LptNum%)
CALL KILLPRINT(LptNum%)
Halts LptNum%, by resetting and purging buffer. Printer reset to defaults
all non-default formats lost. Will do nothing if errors noted, printer
is not busy.
=================================================================
DECLARE FUNCTION MEM2INT% (SegAddress%,OffAddress%)
Reads word from memory and returns an integer value
Faster than PEEK(High_byte) * 256 + PEEK(Low_byte)
=================================================================
DECLARE SUB MEM2STRING (TEXT$,SegAddress%,OffAddress%)
Reads bytes from memory and stores them into a string
number of bytes transferred is = LEN(Text$)
Assumes string is in near data (DGROUP) & not a fixed length string
or a user defined TYPE.
=================================================================
DECLARE SUB OTHERMEMORY(EXTENDED%,EXPANDED%,XMS%)
Returns size of extended, expanded, and XMS memory.
Extended = only size that BIOS reports is free
Expanded = total installed.
XMS = total installed.
Uses the rom ID byte approach to determine if extended memory supported.
=================================================================
DECLARE SUB OTHEROPER (DPMI%, WINDOWS%, DESQ%)
CALL OTHEROPER(DPMI%, WINDOWS%, DESQ%)
Purpose: Checks if DPMI, or MS Windows or Quarterdeck DESQVIEW is active
Returns:
For each variable that exists -1
Else if multitasking environment does not exist then returns 0
Returns 0 if used with DOS versions < 3.x
================================================================= DECLARE FUNCTION PRINTRDY%(Lpt%)
Input:
Lpt% gives portnumber to test
Lpt%=1 for LPT1:, 2 for LPT2:, etc
Returns:
0 if not ready
-1 (True) if ready
=================================================================
DECLARE SUB PRINTSCREEN%(Mode%, Error%)
IF Error% THEN
printer error
else
all o.k.
end if
Input: 0 = turn off printscreen key
1 = printscreen, then shut down key again
Prints to LPT1
=======================================================================
DECLARE SUB REVERSESTRING (A$)
will reverse A$
DECLARE SUB LEFTROTATE (A$)
will rotate a string one character to the left
DECLARE SUB RIGHTROTATE (A$)
will rotate a string one character to the right
=================================================================
DECLARE SUB SETDRIVE (DRIVE$,ErrCode%)
no change occurs if DRIVE$ <"A" or >"z", or if >"Z" & less <"a"
Returns ErrCode% = 0 (False) if no errors
= -1 (True) if errors
error checking includes range check and real test if a change occurred
=================================================================
DECLARE SUB SUBEXIST (PATH$+CHR$(0),ErrCode%)
Returns ErrCode% = 0 (False) if no errors,
= -1 (True) if errors
If a drive change is required, subdirectory on other drive will be made
the "current" subdirectory on that other drive.
PATH$ cannot be a TYPEd string or a variable in a string array
because there is a chance that string will be outside DGROUP. String
cocentenation is used to make sure it is in DGROUP.
=================================================================
DECLARE SUB SUBSTDRIVE (Drive$,ErrCode%)
CALL SUBSTDRIVE(Drive$,ErrCode%)
Input: Drive$ = letter between A to Z
If Drive$ ="" or is outside range, assume it is a request for
default drive information.
Purpose: To warn programmer that the logical drives may not be as they
seem, so if programmer desires to do some technical manipulation
he will be aware that it may not work.
Returns:
0 = o.k.
1 = invalid drive
2 = SUBST active on drive
=================================================================
DECLARE SUB TRUENAME (OrigFile$+CHR$(0), TrueFName$, FileLength%)
Purpose:
Allows user to test for SUBST, ASSIGN, and JOIN using an
undocumented call for DOS Version 3.xx and above
The returned name will contain technically correct Drive: and Path
information. However, the OrigFile$ is not checked so ? and * may be
used, the OrigFile$ may even be nonexistent.
Shortcuts:
OrigFile$ = "." to obtain current drive & subdirectory name
OrigFile$ = ".." to obtain immediate parent drive & subdirectory name
Usage:
TrueFName$ = STR$(67,0)
CALL TRUENAME(OrigFile$ + CHR$(0), TrueFName$, FileLength%)
IF FileLength% = -1 THEN
Error
ELSE
TrueFName$=LEFT$(TrueFName$, FileLength%)
END IF
=================================================================
QBASIC-MASM Interface Theory:
Generally QBASIC only keeps simple variables, (INT,
LONGINT, SINGLE and DOUBLE, and STRINGS) in DGROUP. Arrays and
TYPE records are usually kept outside DGROUP, with only the
STRING Descriptor or the undocumented Array descriptor kept in
DGROUP. Thus, the library routines assume that all information
passed from simple variables is information about a near (DGROUP)
address. If we pass simple integers to MASM routines, and don't
care to have MASM change those variables, we use BYVAL to send
the information to MASM. BYVAL speeds up variable access by a
factor of two. If we want to have MASM send information back
about multiple variables, we must pass the address information
for the variables without using BYVAL. If we use arrays, we must
use a combination of BYVAL & VARSEG and BYVAL & VARPTR to get the
information we need to manipulate the information inside the
arrays. VARSEG and VARPTR are necessary because there is a very
great chance that the arrays will be stored as far data (i.e. notstored in DGROUP). If you must get an array in DGROUP, put the
array in COMMON.
Text strings are sent by QBASIC as near address
(relative to DS and DGROUP). The fist value in the descriptor is
the length of the string. The second value is the offset address
inside DGROUP. Fixed length strings and TYPE record strings are
not referenced with string descriptors. For that reason, I don't
allow such strings as variables in my routines.
PDS/QBX Version 7.x stores strings in FAR DATA, outside
DGROUP. Special routines are offered in the programmer's package
to find address and length of far strings. Not having access to
that program (insufficient liquid assets), I can't suggest work
arounds (I am sure that there are some library routines that will
give you access). My guess is that string input will work if the
following format is used
Inside QBX:
DECLARE SUB xx(BYVAL SEGADD%, BYVAL OFFADD%, BYVAL
LENGTH%, ...)
CALL xx(SSEG(A$), SADD(A$), LEN(A$), ...
Inside MASM:
.code
STRING SEGMENT AT 0h ;used only for MASM assumptions
STRING ENDS ;no code is generated.
xx PROC FAR BASIC USES ...,SEGADD:WORD, OFFADD:WORD, LENGTH:WORD
Push DS
...
Mov SI,OFFADD ;offset of string
Mov CX,LENGTH ;length of string
Mov AX,SEGADD ;segment address of far string
;segment
Mov DS,AX
Assume DS:STRING ;note lose access to stack
when change DS
...
Pop DS
Assume DS:@data
...
Ret
xx ENDP
END
Another approach, which I have not tested is to rely on
the library routines included inside the QBX environment.
Use the following library calls:
EXTRN StringAddress:FAR
EXTRN StringLength:FAR
How to use StringLength (must clear direction flag CLD):
Basic program must pass address of string descriptor on stack
Push StringDescriptor ; push it back on stack
call StringLength ; call routine
length returned in AX
How to use StringAddress (must clear direction flag CLD):
Basic program must pass address of string descriptor on stack
Push StringDescriptor ; push it back on stack
call StringAddress ; call routine
address returned in DX:AX ; DX has segment address, AX has
; offset
QBASIC requires that MASM preserve BP, SI, DI, DS, and
keep the direction flag clear (CLD). All these routines do this.
You will note that MICROSOFT'S CALL INTERRUPT routines do not
save BP (apparently to give you access to some EGA VGA palette
and character font selection routines in the BIOS), and thus will
crash if a critical error occurs. MICROSOFT offers a replacement
routine that overcomes this error by preventing any change to BP.
You should get it if you don't already have it.
The choice of defining MASM routines as SUB's or
FUNCTION's depends on whether you want to get information from
MASM in DX:AX (the format for FUNCTIONs), or if the MASM routine
either sends back no information or more than one variable (the
format for SUBs). For simple one variable routines when MASM is
just returning a value, use FUNCTIONs. To use such functions
inside QBASIC you must either assign the value of the function to
a QBASIC variable or use 1) IF ... THEN statements (C style), or
2) PRINT statements. For everything else use SUBS.
Note due to an error in the QBASIC text parser, always
use the CALL keyword to call the library routines if there is any
chance that you will use a colon as a separator on a line AND the
library routine does not take any parameters. Without the CALL
keyword, the QBASIC parser assumes that the routine name followed
by a colon is intended as a line label, and not as a SUB name.
You must use CALL if you will use a SUB that does not need any
parameters and will follow that SUB name by a colon.
MASM Theory:
The MASM routines were designed to be compiled with MS
QUICK ASSEMBLER, using simplified segment names. They should
compile with MS MASM 5.1 and above. The DOSSEG keyword might not
be supported by your version of MASM, so use the MASM keyword
that arranges the segments in DOS order, rather than ALPHA order.
If you use TASM with QUIRKS (IDEAL too?) you should be able to
compile these programs with TASM. Because A86 does not make MS
.OBJ files, you probably cannot use it. The same may be true
with OPTASM. The @@, @f, @b are local labels used so I can skip
ahead without having to think up unique names. Don't use @@, @f,
@b inside MASM Macros, use LOCAL alphanumeric names instead.
MASM too easily can lose track of which local label you mean.
The simplified directives really save time when you
have to identify variables on the stack, and push and pop
variables. I use the full PROC function and the regular PROCkeywords interchangeably. Full PROC format is not necessary when
there is no need to address variables on the stack (i.e. no need
to change BP to address the stack) and the QBASIC routine is
described as a FUNCTION or a SUB that does not take or return
parameters..
The EVEN directive is used to word align loops for
80286 and 80386 machines. EVEN inserts NOPS as necessary to word
align loops. The QBASIC BYVAL directive is used to get
information directly from QBASIC without having to use [BX] or
[SI] to fish out the correct information. To allow for
compatibility with the 8088 chip, shifts rather than MULs are
used for speed. I use .code to store most of my variables to
save space in DGROUP, which is comparatively tiny and easily
filed with string data.
To program in OS/2 .code segment variables are
"verbotten", and you must use local variables on the stack. I
don't have access to the OS/2 DOS compatibility box, so I don't
know how these routines would work under OS/2. I am certain that
none of these routines would work smoothly under protected mode
because they often access the BIOS or the hardware.
Bibliography:
General DOS/BIOS routines: Ray Duncan and Peter Norton's
programming books. If you can only afford one book, get Duncan's.
Duncan, "Advanced MS-DOS Programming" (2d Ed. Microsoft 1988). Very
easy to read and to refer to specific routines. The text is very well
formatted for easy reference.
Norton, "The New Peter Norton Programmer's Guide to the IBM PC
and PS/2" (Harper & Row 1988). A book designed to help you visualize
what is going on. Having been "burned" by writing a book on the PC
Junior, and experienced the agony of hardware incompatibilities in
writing the Norton Utilities, Peter wants everybody to use the bios and
documented DOS routines.
Bradley, "Assembly Language Programming for the IBM Personal
Computers" (Prentice Hall 1984). Good technical descriptions of the
original PC. Only book I have seen that has really shown how to switch
displays, manipulate DMA, make sound, and manipulate floppy disk drives
at the PORT level (teaches you why we have BIOS and DOS routines). Good
book on the 8087. Probably now out of print. I bought my copy at a
book store that sells remainders.
DOS Programming:
Detteman, "DOS Programmer's Reference (2d Ed. Que 1989).
Excellent book with a very helpful look at undocumented DOS. Doesn't
preach about not using undocumented DOS as does Peter Norton. Goes
"mano a mano" with DOS. You should read that material for a complete
discussion of the undocumented calls contained in my routines.
Waite Group "Dos Developer's Guide" (2d Ed Howard Sams 1989).
Good book on Expanded memory and math chips. Several good discussions
of the undocumented DOS functions. Such a big book, that you feel you
got your money's worth based just on weight.
General Programming:
Holzner, "PS/2-PC Assembly Language" (Brady 1989) Good
introduction to assembly language programming. In many respects muchbetter than Holzner, "Advanced Assembly Language on the IBM PC" (Brady
1987).
Tischer, "PC System Programming for Developers" (ABACUS 1989).
Good technical survey of PC/AT systems. Comes with a lot of programs
showing how to do useful things in MASM, GWBASIC, PASCAL and C. The
only shortcoming is that the book is printed on very cheap paper. One
of the few books to show register usage of DOS and BIOS routines
(important so you can save variables in registers rather than in memory
or pushed on the stack.) Doesn't discuss DOS Version 4 in an integrated
fashion. Such a big book, that you feel you got your money's worth
based just on weight.
Young, "Inside DOS: A Programmer's Guide", (Sybex 1990). Good
source of MASM code, with a primary emphasis on C (The MASM code is
similar for both C and QBASIC). A reprint of "MS DOS Advanced
Programming" (Sybex 1988). (I didn't discover this until I purchased
both).
Wyatt, "Using Assembly Language" (2d Ed. QUE 1989). Some decent
video routines. No useful description of VGA. EGA routines are
primitive. Good insight into programming for high level languages.
Complete listing of all the Intel keywords, but reads too much like an
Intel-English dictionary. Most of the examples in the keyword section
are not really helpful and the ASCII math routines are not well
described. One major programming feature dropped from the 1st edition
was the AT keyboard programming routines. Does this mean AT keyboards
are only marginally compatible on the hardware level? Feedback please.
QBASIC Programming:
Goodwin, "QuickBASIC Advanced Programming Tools" (MIS 1989). A
much better book for QBASIC code (sorting, popup windows, dropdown
windows and menubar programs) than for MASM code. The MASM code relies
on video bios to do things (which is slow, though always compatible).
Moreover, screens are stored in STRINGs rather than arrays, which
greatly limits the usefulness of his MASM routines. (I agree storing
screens in arrays takes more work). A programmer can have access to
several hundred KB of far data in arrays, while he can only have access
to 30 to 40kb of precious string space in DGROUP. PDS 7.1 gives access
to about 190(?) kb of string space using far strings. Goodwin's book
was written before PDS was released.
Lesser, "Advanced Quickbasic 4.0 Language Extensions with
Modular Tools" (Bantam 1988). This is how I learned about MASM/QBASIC
programming. Talks more about ideas that were important for QBASIC
Version 3.0 -- MASM pre-version 5.1 (before simplified directives) than
for QBASIC Version 4.x. BELIEVES in structured programming. Not too
many hardware insights even though hardware insights are necessary for
fast video programming. Offers generally sound advice.
Microsoft, "QuickBasic Toolbox" Generally, useful collection of
routines. Very speedy string routines, some good formatting and a good
set of long int days between dates routines. Some little insight into
Quick C - Basic interfacting using Quick C library routines. The DOS
InterruptX routines are generally useful, however, they do not warn of a
problem inside QBASIC that can cause QBASIC to lock up. The error
occurs if a critical error (typically disk error) occurs inside a Call
Interrupt/InterruptX routine.
Reason for error: Because the Microsoft supplied version of that
routine allows the programmer to change BP, any critical error causesQBASIC to get lost. All smart programmers should obtain, from
Microsoft's on-line knowledgebase on GENIE and COMPUSERVE, a copy of the
revised routine and NOCOM.OBJ (makes some programs smaller by turning
off all of QBASIC overhead associated with the COM ports) from
Microsoft. The revised Interrupt/InterruptX routines prevents any
change to BP at the cost of a minor loss of funtionality in EGA/VGA font
routines.
Shammas, "QuickBASIC- Programming Techniques and Library
Development" (M&T Publishing 1988). Good QBASIC code. Some of the
string routines are fast. He has some BTREE and high level math
What's available:
VIDBASIC - A selection of text mode video routines. Freeware.
The library allows the user to select, move, switch, change, save,
restore, draw boxes, and write with lightening speed. MASM .ASM code,
.OBJ, and a demo routine with source is included. Uploaded by author on
GENIE and COMPUSERVE.
KEYBASIC - A selection of Microsoft Compatible Mouse and
Keyboard utilities. Text mode mouse utilities. Fast mouse utilities.
Turn off CONTROL-BREAK so you can use LINE INPUT$ without error if user
pushes the Control-Break key. Freeware. MASM .ASM code, .OBJ, and a
demo routine with source is included. Uploaded by author on GENIE and
COMPUSERVE.
I don't anticipate I will do much more with any of these three
libraries. My next effort is to make menu environment similar to the
environment inside QuickBasic, using the building blocks I have created.
After that, I will probably try to figure out Quick C and convert much
of the library to Quick C format. I figure by the time I begin to
understand Quick C, an OOPS form of Quick Basic will come out and much
of my work will be for naught.
As always, constructive criticism is desired. These libraries
were released as Freeware to encourage a sharing of ideas. If you have
something to say, I welcome your comments. With application programs
there appears to be a significant and speedy exchange of ideas,
programmers, on the other hand, seem to move at a much slower pace, with
Jim Mack being the notable exception.